// STL
#include <string>
#include <cstring>
#include <iostream>

// LIBXML
#include <libxml/parser.h>

// QT
#include <QDir>
#include <QFile>
#include <QUrl>
#include <QDomDocument>
#include <QDomElement>
#include <QDebug>
#include <QMetaProperty>
#include <QString>
#include <QApplication>
#include <QThread>

// CFU
#include <errors.hpp>
#include <textures.hpp>

void startDocument(void *user_data)
{
  // std::cout<< "@@@ Start Docunent"<< std::endl;
}

void endDocument(void *user_data)
{
  // std::cout<< "@@@ End Docunent"<< std::endl;
}

void startElement(void * user_data, const xmlChar * name, const xmlChar **attrs)
{
  xmlParser_t &parser = *((xmlParser_t *)user_data);
  parser.startElement((const char *)name, (const char **)attrs);
}

void endElement(void *user_data, const xmlChar *name)
{
  xmlParser_t &parser = *((xmlParser_t *)user_data);
  parser.endElement((const char *)name);
}

static void my_warning(void *user_data, const char *msg, ...) 
{
  va_list args;

  va_start(args, msg);
  printf(msg, args);
  va_end(args);
}

static void my_error(void *user_data, const char *msg, ...) 
{
  va_list args;

  va_start(args, msg);
  printf(msg, args);
  va_end(args);
}

static void my_fatalError(void *user_data, const char *msg, ...) 
{
  va_list args;

  va_start(args, msg);
  printf(msg, args);
  va_end(args);
}

///// / / / Build handler 

static xmlSAXHandler my_handler =
  {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    startDocument, //  startDocumentSAXFunc startDocument;
    endDocument, //  endDocumentSAXFunc endDocument;
    startElement, //  startElementSAXFunc startElement;
    endElement, //  endElementSAXFunc endElement;
    NULL,
    NULL, //  charactersSAXFunc characters;
    NULL,
    NULL,
    NULL,
    my_warning,
    my_error,
    my_fatalError
  };


///// / / /  Parsing entry function

bool check_filenames(const char *filename, struct checker *chk) 
{
  ///// / / / Starting parsing
  
  xmlParser_t *parser = new xmlParser_t (chk); 

  if (xmlSAXUserParseFile(&my_handler, (void *)parser, filename) < 0)
    {
      std::cout << "Error parsing the file " << filename << std::endl;
      delete parser; 
      return false;x
    }
  
  delete parser; 
  return true; 
}

///// / / / Define the xmlParser 

xmlParser_t::xmlParser_t (struct checker *chk): 
  _chk(chk),
  current (0), 
  level (0)
{ 
  pushState (startEl_document, endEl_document); 
} 

void xmlParser_t::pushState(startElement_cb start, endElement_cb end, void *userdata)
{
  parserState_t state;
  state.start = start;
  state.end = end;
  state.userdata = userdata;
  state.level = level;
  state_stack.push_back(state);
  current = &state_stack.back();
}

void xmlParser_t::popState()
{
  state_stack.pop_back();
  if(!state_stack.empty()) current = &state_stack.back();
  else current = 0;
}

/*=============================================================
  / start- and endElement callbacks for the different states
  =============================================================*/

void endEl_dummy(xmlParser_t &parser, const char *element)
{	parser.popState();	}

void startEl_dummy(xmlParser_t &parser, const char *element, const char **attrs)
{	parser.pushState(startEl_dummy, endEl_dummy);	}

void startEl_document(xmlParser_t &parser, const char *element, const char **attrs)
{


      parser.pushState(startEl_scene, endEl_scene);
      //std::cout << "starting <scene>" << std::

}

void endEl_document(xmlParser_t &parser, const char *element)
{
 
};

void startEl_scene(xmlParser_t &parser, const char *element, const char **attrs)
{
  std::string el(element), *name=0;
  if( el == "material" 
      || el == "integrator" 
      || el == "light" 
      || el == "texture" 
      || el == "camera" 
      || el == "background" 
      || el == "object")
    {
      if(!attrs[0]){ std::cerr << "error: no attributes for scene element given!" << std::endl; return; }
      else if(!strcmp(attrs[0], "name")) name = new std::string(attrs[1]);
      else{ std::cerr << "error: attribute for scene element does not match 'name'!" << std::endl; return; }
      parser.pushState(startEl_parammap, endEl_parammap, name);
      std::cout << "starting <" << el << ">" << std::endl;
    }
  else if(el == "mesh")
    {
      parser.pushState(startEl_mesh, endEl_mesh, NULL);
    } 
  else if(el == "smooth")
    {
      parser.pushState(startEl_dummy, endEl_dummy);
    }
  else if(el == "render")
    {
      parser.pushState(startEl_parammap, endEl_render);
    }
  else std::cout << "skipping unrecognized scene element" << std::endl;
}

void endEl_scene(xmlParser_t &parser, const char *element)
{
  if(strcmp(element, "scene")) std::cerr << "warning: expected </scene> tag!" << std::endl;
  else
    {
      parser.popState();
    }
}

// mesh-state, i.e. expect only points (vertices), faces and material settings
// since we're supposed to be inside a mesh block, exit state on "mesh" element


void startEl_mesh(xmlParser_t &parser, const char *element, const char **attrs)
{
  return; 
}

void endEl_mesh(xmlParser_t &parser, const char *element)
{
  if(std::string(element) == "mesh")
    {
      parser.popState();
    }
}



// read a parameter map; take any tag as parameter name
// again, exit when end-element is on of the elements that caused to enter state
// depending on exit element, create appropriate scene element

void startEl_parammap(xmlParser_t &parser, const char *element, const char **attrs)
{
  // support for lists of paramMaps
  if(std::string(element) == "list_element")
    {
      parser.pushState(startEl_paramlist, endEl_paramlist);
      return;
    }

  if(std::string (element) == "filename")
    {
      QFile img(attrs[1]) ; 

      if (!img.exists ()) 
	{
	  std::string filename(attrs[1]); 
	  std::cout << "@@@ Panic, file " << filename << " is missing\n"; 
	  // / / / Here we have to return an error
	  parser._chk->status = 0; 
	  parser._chk->msg = strdup (attrs[1]); 
	  return ; 
	}
    }

}

void endEl_parammap(xmlParser_t &p, const char *element)
{
  bool exit_state= (p.currLevel() == p.stateLevel());	
  if(exit_state)
    {
      std::string el(element);
      std::string *name = (std::string *)p.stateData();
      if(!name) std::cerr << "error! no name for scene element available!" << std::endl;
      else {}
	/* {
	   if(el == "material")
	   { 
	   p.env->createMaterial(*name, p.params, p.eparams);  }
	   else if(el == "integrator")
	   { 
	   p.env->createIntegrator(*name, p.params);
	   }
	   else if(el == "light")
	   {
	   light_t *light = p.env->createLight(*name, p.params);
	   if(light) p.scene->addLight(light);
	   }
	   else if(el == "texture")
	   { 
	   p.job->addTexture(*name, p.params); 
	   p.env->createTexture(*name, p.params); // Tricky and risky, dude .. 
	   }
	   else if(el == "camera")
	   { 
	   p.env->createCamera(*name, p.params); 
	   }
	   else if(el == "background")
	   {
	   std::cout << "Background added" << std::endl ; 
	   p.env->createBackground(*name, p.params);
	   std::cout << "Backgroud added in yadirefa file" << std::endl ; 
	   }
	   else if(el == "object")
	   {
	   objID_t id;
	   object3d_t *obj = p.env->createObject(*name, p.params);
	   if(obj) {
	   p.scene->addObject(obj, id);
	   } 
	   }
	   else std::cerr << "warning: unexpected end-tag of scene element!\n";
	   }*/
	
      if(name) delete name;
      p.popState(); 
      //std::cout << "ending <" << el << ">" << std::endl;
    }
}

void startEl_paramlist(xmlParser_t &parser, const char *element, const char **attrs)
{
  std::cout << "adding parameter '" << element << "'" << std::endl;
}

void endEl_paramlist(xmlParser_t &p, const char *element)
{
  if(std::string(element) == "list_element")
    {
      p.popState();
    }
}

void endEl_render(xmlParser_t &p, const char *element)
{
  if(!strcmp(element, "render"))
    {
      p.popState();
      std::cout << "ending <render>" << std::endl;
    }
}


